/*
  Copyright (C) 2006 by Lukas Loehrer

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

  Author: Lukas Loehrer <loehrerl [at] gmx [dot] net>
*/

// Various utility functions
var _jslib= null;
function jslib() {
	if (!_jslib) {
		_jslib = Components.classes["@mozilla.org/url-classifier/jslib;1"].getService().wrappedJSObject;
	}
	return _jslib;
}

// {{{ Some utilities to lookup and instantiate classes by name.

var _CI = Components.interfaces;
var _CC = Components.classes;

function make (class_name, interface_name) {
	return _CC[class_name].createInstance (_CI[interface_name]);
}

function service (class_name, interface_name) {
	return _CC[class_name].getService (_CI[interface_name]);
}

// }}}
// {{{Functions for working with chrome windows 

function listOpenWindows(type) {
	type = type || "";
	var wm = service("@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");

	var enumerator = wm.getEnumerator(type);
	var wl = [];
	while(enumerator.hasMoreElements()) {
		var win = enumerator.getNext();
		wl.push(win);
	}
	return wl;
}

function listOpenXULWindows(type) {
	type = type || "";
	var wm = service("@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");

	var enumerator = wm.getXULWindowEnumerator(type);
	var wl = [];
	while(enumerator.hasMoreElements()) {
		var win = enumerator.getNext();
		wl.push(win);
	}
	return wl;
}

function openWindow(url, name, features, args) {
	args = args || null;
	features = features || "chrome,centerscreen";
	name = name || null;

	var ww = service("@mozilla.org/embedcomp/window-watcher;1", "nsIWindowWatcher");
	var win = ww.openWindow(null, url, name, features, args);
	return win;
}

/* This is a hack!
   The intent is to get a new chrome window named name
   whose UI is already loadded without fiddling with "load" event handlers.
   Especially, "getBrowser()" should work.
   Cleaner solutions are very welcome.
*/
function openReadyWindowWithName(name) {
	var parent = getMostRecentWindow();

	var win = getMainWindow(parent.open("about:blank", "_blank"));
	win.name = name;
	return win;
}



function getWindowByName(name) {
	var ww = service("@mozilla.org/embedcomp/window-watcher;1", "nsIWindowWatcher");
	var win = ww.getWindowByName(name, null);
	return win;
}

function getMostRecentWindow(type) {
	type = type || "navigator:browser";
	var win = service("@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator")
		.getMostRecentWindow(type);
	return win;
}

// Returns the top most ancestor in the document tree of "win", usually a XUL window
function getMainWindow(win) {
	var mainWindow = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
		.getInterface(Components.interfaces.nsIWebNavigation)
		.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
		.rootTreeItem
		.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
		.getInterface(Components.interfaces.nsIDOMWindow) ;
	return mainWindow;
}

/* Return the browser element for a given DOM node, document or window.
   This assumes a tabbrowser is present in the chrome window.
*/
function getBrowserForDOMNode(aNode) {
	var doc, win;
	if (aNode instanceof _CI.nsIDOMDocument) {
		doc = aNode;
		win = doc.defaultView;
	} else if (aNode instanceof _CI.nsIDOMWindow) {
		doc = aNode.document;
		win = aNode;
	} else {
		doc = aNode.ownerDocument;
		win = doc.defaultView;
	}
	if (doc && win) {

		var mainWin = getMainWindow(win);
		if (mainWin) {
			var tb = mainWin.getBrowser();
			if (tb) {
				return tb.getBrowserForDocument(doc);
			}
		}
		
	}
	return null;
}


// }}}
// {{{Event driven programming 
   
function withDocumentLoaded(contentPanel, url, action) {
	//var contentPanel = getBrowser().selectedBrowser;
	contentPanel.addEventListener(
								  'load', function(event) {
		if(event.target != contentPanel.contentDocument)
			return;
    
		contentPanel.removeEventListener('load', arguments.callee, true);
    
		action(contentPanel.contentDocument);
	}, false);
    
	contentPanel.contentDocument.location.href = url;    
}

	// }}}
// {{{String encoding conversion

function convertToIsoLatin1 (string)  {
	var s =	service("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
	s.charset = "ISO8859-1";
	return s.ConvertFromUnicode  (string) + s.Finish();
}

function convertToUtf8(string) {
	var s =	service("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
	s.charset = "utf-8";
	return s.ConvertFromUnicode  (string) + s.Finish();
}


// }}}
// {{{Functional programming 

// Returns its arbument unchanged (for testing)
function identity(arg) {
  return arg;
}


// Binds a method to a desired receiver object (there must be a built in way to do this I am not aware of.)
function bindMethod(theThis, method) {
	var _this = theThis;
	return function () {
		return fun.apply(_this, arguments);
	};
}

// }}}
	// {{{ 

/* Iterator for anything that looks like an Array.
Especially useful for DOM node lists that normally do not support "for x in list"
*/
function ArrayIterator(array) {
	var len = array.length;
	for (var i = 0; i < len; ++i) {
		yield array[i];
	}
}

	// }}}

// {{{Functions for generating lookup tables for  names of numeric constants

function  initConstantNames(obj, prefix) {
	var prefixLength = prefix.length;
	var map = {};
	for (var name in obj) {
		if (name.substring(0,prefixLength) == prefix) {
			map[obj[name]] = name;
		}
	}
	return map;
}

// map is a hash table mapping flag values to flag names
// returns a list of flag names that VALUE matches
function checkedFlagNames(map, value) {
	var matchList = [];
	for (var flag in map) {
		if ((value & flag) != 0) {
			matchList.push(map[flag]);
		}
	}
	return matchList.join(", ");
}

	// }}}


/* This is probably unsafe and ntot the best idea, but those wrappers are really annoying... */
function unwrap(obj) {
	if (obj.wrappedJSObject)
		return obj.wrappedJSObject;
	else
		return obj;
}

// Running callback in a timer
function doLater(callback, delay) {
	delay = delay || 0;
	var timer = make('@mozilla.org/timer;1', "nsITimer");
	timer.initWithCallback(
	{
		notify: callback
					}, delay, Ci.nsITimer.TYPE_ONE_SHOT);
	return timer;
}
